﻿using Ioacp.Library.Helpers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;

namespace Ioacp.HikVision
{
    /// <summary>
    /// 封装海康威视相关 SDK
    /// </summary>
    public class HikVisionSDK : IDisposable
    {
		public static int m_lRealHandle = -1;

		private static int m_lAlarmHandle = -1;

		private CHCNetSDK.MSGCallBack m_falarmData = null;

		public static int m_lUserID = -1;

		public static Queue<string> queue_louder = new Queue<string>();

		public static Queue<string> queue_alarm = new Queue<string>();     // 报警信息

		private static HikVisionInitEventArgs _loginModel;

		public event EventHandler<string> OnFaceDetectionChanged;

		private IntPtr _videoPreviewWnd;

		public IntPtr VideoPreviewWnd 
		{
			get { return _videoPreviewWnd; }
			set
			{
				_videoPreviewWnd = value;

				if (_videoPreviewWnd == IntPtr.Zero) return;

				if (m_lRealHandle >= 0)
				{
					//停止预览 Stop live view 
					CHCNetSDK.NET_DVR_StopRealPlay(m_lRealHandle);
					m_lRealHandle = -1;
				}

				// 不在预览状态
				if (m_lRealHandle < 0)
				{
					CHCNetSDK.NET_DVR_PREVIEWINFO lpPreviewInfo = new CHCNetSDK.NET_DVR_PREVIEWINFO();
					lpPreviewInfo.hPlayWnd = _videoPreviewWnd;//预览窗口 live view window
					lpPreviewInfo.lChannel = _loginModel.Channel;//预览的设备通道 the device channel number11
					lpPreviewInfo.dwStreamType = 0;//码流类型：0-主码流，1-子码流，2-码流3，3-码流4，以此类推
					lpPreviewInfo.dwLinkMode = 0;//连接方式：0- TCP方式，1- UDP方式，2- 多播方式，3- RTP方式，4-RTP/RTSP，5-RSTP/HTTP 
					lpPreviewInfo.bBlocked = true; //0- 非阻塞取流，1- 阻塞取流
					lpPreviewInfo.dwDisplayBufNum = 15; //播放库显示缓冲区最大帧数

					IntPtr pUser = IntPtr.Zero;//用户数据 user data 

					m_lRealHandle = CHCNetSDK.NET_DVR_RealPlay_V40(m_lUserID, ref lpPreviewInfo, null/*RealData*/, pUser);
				}
			}
		}

		private static CHCNetSDK.MSGCallBack_V31 _keepRun;

		#region Init

		/// <summary>
		/// 初始化
		/// </summary>
		/// <param name="loginModel"></param>
		public void Init(HikVisionInitEventArgs loginModel)
		{
			try
			{
				// 释放海康威视资源包
				if (!File.Exists("HCCore.dll")) ResHelper.ExtractResFile("Ioacp.HikVision.Resources.HCCore.dll", "HCCore.dll");
				if (!File.Exists("HCNetSDK.dll")) ResHelper.ExtractResFile("Ioacp.HikVision.Resources.HCNetSDK.dll", "HCNetSDK.dll");
				if (!File.Exists("hpr.dll")) ResHelper.ExtractResFile("Ioacp.HikVision.Resources.hpr.dll", "hpr.dll");
				if (!File.Exists("libeay32.dll")) ResHelper.ExtractResFile("Ioacp.HikVision.Resources.libeay32.dll", "libeay32.dll");
				if (!File.Exists("ssleay32.dll")) ResHelper.ExtractResFile("Ioacp.HikVision.Resources.ssleay32.dll", "ssleay32.dll");
				if (!File.Exists("zlib1.dll")) ResHelper.ExtractResFile("Ioacp.HikVision.Resources.zlib1.dll", "zlib1.dll");
				if (!File.Exists("HCNetSDKCom.zip")) ResHelper.ExtractResFile("Ioacp.HikVision.Resources.HCNetSDKCom.zip", "HCNetSDKCom.zip");

				// 解压缩包
				if (!Directory.Exists("HCNetSDKCom")) ZipEx.UnZip("HCNetSDKCom.zip", "HCNetSDKCom");

				_loginModel = loginModel;

				// 初始化 SDK, 调用设备网络 SDK 其它函数的前提
				if (!CHCNetSDK.NET_DVR_Init())
				{
					MessageBox.Show("NET_DVR_Init error!");
					LogEx.Error("海康威视初始化失败 NET_DVR_Init Error");
				}

				// 防止资源释放
				_keepRun = FaceDetectionCallback;
				CHCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(_keepRun, IntPtr.Zero);

				// 开始布防人脸侦测
				FaceDetectionBegin(loginModel.IsRKE);
			}
			catch(Exception ex)
			{
				LogEx.Error("海康威视初始化失败", ex);
			}
		}

		#endregion

		#region FaceDetectionCallback

		/// <summary>
		/// 人脸侦测回调
		/// </summary>
		/// <param name="lCommand"></param>
		/// <param name="pAlarmer"></param>
		/// <param name="pAlarmInfo"></param>
		/// <param name="dwBufLen"></param>
		/// <param name="pUser"></param>
		public void FaceDetectionCallback(int lCommand, ref CHCNetSDK.NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
		{
			try
			{
				//if (lCommand != 10498) return;

				CHCNetSDK.NET_VCA_FACESNAP_MATCH_ALARM nET_VCA_FACESNAP_MATCH_ALARM = default(CHCNetSDK.NET_VCA_FACESNAP_MATCH_ALARM);
				nET_VCA_FACESNAP_MATCH_ALARM = (CHCNetSDK.NET_VCA_FACESNAP_MATCH_ALARM)Marshal.PtrToStructure(pAlarmInfo, typeof(CHCNetSDK.NET_VCA_FACESNAP_MATCH_ALARM));
				if (nET_VCA_FACESNAP_MATCH_ALARM.struSnapInfo.struDevInfo.byIvmsChannel != _loginModel.Channel) return;
				if (nET_VCA_FACESNAP_MATCH_ALARM.fSimilarity <= 0f)
				{
					if (OnFaceDetectionChanged != null)
						OnFaceDetectionChanged.Invoke(this, "\0\0\0\0\0\0");
					return;
				}

				string text = "\0\0\0\0\0\0";
				text = GetFaceCardNumber(nET_VCA_FACESNAP_MATCH_ALARM.struBlackListInfo.struBlackListInfo.struAttribute.byCertificateNumber);

				//LogEx.Error("人脸:" +  text);

				if (OnFaceDetectionChanged != null)
					OnFaceDetectionChanged.Invoke(this, text);
			}
			catch (Exception ex)
			{
				LogEx.Error("海康威视人脸回调失败", ex);
			}
		}

		#endregion

		#region GetFaceCardNumber

		/// <summary>
		/// 获取人脸识别的卡号
		/// </summary>
		/// <param name="certificate"></param>
		/// <returns></returns>
		private string GetFaceCardNumber(byte[] certificate)
		{
			try
			{
				string @string = Encoding.Default.GetString(certificate);
				int num = 0;
				for (int i = 0; i < certificate.Length; i++)
				{
					if (certificate[i] == 0)
					{
						num = i;
						break;
					}
				}

				if (num > 0 && num <= 10)
				{
					return @string.Substring(0, num);
				}

				return "\0\0\0\0\0\0";
			}
			catch
			{
				return string.Empty;
			}
		}

		#endregion

		#region FaceDetectionBegin

		/// <summary>
		/// 人脸侦测布防开始
		/// </summary>
		/// <param name="isRKE">是否使用门禁型API，不使用则使用枪式API</param>
		private void FaceDetectionBegin(bool isRKE)
		{
			try
			{
				if (m_lUserID >= 0 || FaceDetectioDeviceLogin())
				{
					CHCNetSDK.NET_DVR_SETUPALARM_PARAM lpSetupParam = default(CHCNetSDK.NET_DVR_SETUPALARM_PARAM);
					lpSetupParam.dwSize = (uint)Marshal.SizeOf((object)lpSetupParam);
					lpSetupParam.byLevel = 0;			// 布防优先级：0 - 一等级（高），1 - 二等级（中），2 - 三等级（低）
					lpSetupParam.byAlarmInfoType = 1;	// 报警信息上传类型：0 - 老报警信息（NET_DVR_PLATE_RESULT），1 - 新报警信息(NET_ITS_PLATE_RESULT)
					lpSetupParam.byFaceAlarmDetection = 0;	// 布防类型(仅针对门禁主机、人证设备)：0 - 客户端布防(会断网续传)，1 - 实时布防(只上传实时数据)
					lpSetupParam.byBrokenNetHttp = 0;
					lpSetupParam.byDeployType = 1;
					lpSetupParam.byAlarmTypeURL = 0;

					// 建立报警布防通道
					m_lAlarmHandle = CHCNetSDK.NET_DVR_SetupAlarmChan_V41(m_lUserID, ref lpSetupParam);

					if (isRKE)
                    {
						// 门禁型通道使用（和枪式摄像仪冲突）
						m_falarmData = new CHCNetSDK.MSGCallBack(MsgCallback);
						if (CHCNetSDK.NET_DVR_SetDVRMessageCallBack_V50(0, m_falarmData, IntPtr.Zero));
					}

					if (m_lAlarmHandle >= 0) return;

					LogEx.Error("设备布防失败.");
					MessageBox.Show("设备布防失败.");
				}

				LogEx.Error("设备访问授权获取失败.");
				MessageBox.Show("设备访问授权获取失败.");
			}
			catch (Exception ex)
			{
				LogEx.Error("人脸侦测布防失败", ex);

			}
		}

		#endregion

		private void MsgCallback(int lCommand, ref CHCNetSDK.NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
		{
			switch (lCommand)
			{
				case CHCNetSDK.COMM_ALARM_ACS:
					ProcessCommAlarmACS(ref pAlarmer, pAlarmInfo, dwBufLen, pUser);
					break;
				default:
					break;
			}
		}

		private void ProcessCommAlarmACS(ref CHCNetSDK.NET_DVR_ALARMER pAlarmer, IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
		{
			CHCNetSDK.NET_DVR_ACS_ALARM_INFO struAcsAlarmInfo = new CHCNetSDK.NET_DVR_ACS_ALARM_INFO();
			struAcsAlarmInfo = (CHCNetSDK.NET_DVR_ACS_ALARM_INFO)Marshal.PtrToStructure(pAlarmInfo, typeof(CHCNetSDK.NET_DVR_ACS_ALARM_INFO));
			CHCNetSDK.NET_DVR_LOG_V30 struFileInfo = new CHCNetSDK.NET_DVR_LOG_V30();
			struFileInfo.dwMajorType = struAcsAlarmInfo.dwMajor;
			struFileInfo.dwMinorType = struAcsAlarmInfo.dwMinor;
			char[] csTmp = new char[256];

			if (CHCNetSDK.MAJOR_ALARM == struFileInfo.dwMajorType)
			{
				TypeMap.AlarmMinorTypeMap(struFileInfo, csTmp);
			}
			else if (CHCNetSDK.MAJOR_OPERATION == struFileInfo.dwMajorType)
			{
				TypeMap.OperationMinorTypeMap(struFileInfo, csTmp);
			}
			else if (CHCNetSDK.MAJOR_EXCEPTION == struFileInfo.dwMajorType)
			{
				TypeMap.ExceptionMinorTypeMap(struFileInfo, csTmp);
			}
			else if (CHCNetSDK.MAJOR_EVENT == struFileInfo.dwMajorType)
			{
				TypeMap.EventMinorTypeMap(struFileInfo, csTmp);
			}

			String szInfo = new String(csTmp).TrimEnd('\0');
			String szInfoBuf = null;
			szInfoBuf = szInfo;
			/**************************************************/
			String name = Encoding.UTF8.GetString(struAcsAlarmInfo.sNetUser).TrimEnd('\0');
			for (int i = 0; i < struAcsAlarmInfo.sNetUser.Length; i++)
			{
				if (struAcsAlarmInfo.sNetUser[i] == 0)
				{
					name = name.Substring(0, i);
					break;
				}
			}

			if (struAcsAlarmInfo.struAcsEventInfo.dwEmployeeNo != 0)
			{
				//szInfoBuf = szInfoBuf + "+EmployeeNo:" + struAcsAlarmInfo.struAcsEventInfo.dwEmployeeNo;
				if (OnFaceDetectionChanged != null)
					OnFaceDetectionChanged.Invoke(this, struAcsAlarmInfo.struAcsEventInfo.dwEmployeeNo.ToString());
			}
		}
			
		#region FaceDetectioDeviceLogin

		private static CHCNetSDK.NET_DVR_USER_LOGIN_INFO pLoginInfo;

		private static CHCNetSDK.NET_DVR_DEVICEINFO_V40 lpDeviceInfo;

		/// <summary>
		/// 人脸设备登录操作
		/// </summary>
		/// <returns></returns>
		public bool FaceDetectioDeviceLogin()
		{
			try
			{
				pLoginInfo = default(CHCNetSDK.NET_DVR_USER_LOGIN_INFO);
				pLoginInfo.byRes1 = 0;

				pLoginInfo.sDeviceAddress = _loginModel.IPAddress;
				pLoginInfo.wPort = _loginModel.Port;
				pLoginInfo.sUserName = _loginModel.UserName;
				pLoginInfo.sPassword = _loginModel.Password;

				pLoginInfo.bUseAsynLogin = false;
				lpDeviceInfo = default(CHCNetSDK.NET_DVR_DEVICEINFO_V40);

				// 用户注册设备
				m_lUserID = CHCNetSDK.NET_DVR_Login_V40(pLoginInfo, ref lpDeviceInfo);
				if (m_lUserID < 0) return false;

				return true;
			}
			catch (Exception ex)
			{
				LogEx.Error("设备访问授权获取失败.", ex);
				return false;
			}
		}

		#endregion

		public MemoryStream CaptureJpeg()
		{
			int lChannel = _loginModel.Channel; //通道号 Channel number

			CHCNetSDK.NET_DVR_JPEGPARA lpJpegPara = new CHCNetSDK.NET_DVR_JPEGPARA();
			lpJpegPara.wPicQuality = 0; //图像质量 Image quality
			lpJpegPara.wPicSize = 0xff; //抓图分辨率 Picture size: 0xff-Auto(使用当前码流分辨率) 
										//抓图分辨率需要设备支持，更多取值请参考SDK文档

			//JPEG抓图保存成文件 Capture a JPEG picture
			string sJpegPicFileName;
			sJpegPicFileName = "filetest.jpg";//图片保存路径和文件名 the path and file name to save

			if (!CHCNetSDK.NET_DVR_CaptureJPEGPicture(m_lUserID, lChannel, ref lpJpegPara, sJpegPicFileName))
			{
				var iLastErr = CHCNetSDK.NET_DVR_GetLastError();
				var str = "NET_DVR_CaptureJPEGPicture failed, error code= " + iLastErr;
				//DebugInfo(str);
				return null;
			}
			else
			{
				var str = "NET_DVR_CaptureJPEGPicture succ and the saved file is " + sJpegPicFileName;
				//DebugInfo(str);
			}

			//JEPG抓图，数据保存在缓冲区中 Capture a JPEG picture and save in the buffer
			uint iBuffSize = 400000; //缓冲区大小需要不小于一张图片数据的大小 The buffer size should not be less than the picture size
			byte[] byJpegPicBuffer = new byte[iBuffSize];
			uint dwSizeReturned = 0;

			if (!CHCNetSDK.NET_DVR_CaptureJPEGPicture_NEW(m_lUserID, lChannel, ref lpJpegPara, byJpegPicBuffer, iBuffSize, ref dwSizeReturned))
			{
				var iLastErr = CHCNetSDK.NET_DVR_GetLastError();
				var str = "NET_DVR_CaptureJPEGPicture_NEW failed, error code= " + iLastErr;
				// DebugInfo(str);
				return null;
			}
			else
			{
				//将缓冲区里的JPEG图片数据写入文件 save the data into a file
				//string str = "buffertest.jpg";
				MemoryStream fs = new MemoryStream();
				int iLen = (int)dwSizeReturned;
				fs.Write(byJpegPicBuffer, 0, iLen);
				fs.Close();

				//str = "NET_DVR_CaptureJPEGPicture_NEW succ and save the data in buffer to 'buffertest.jpg'.";
				//DebugInfo(str);
				return fs;
			}
		}

		#region Dispose

		/// <summary>
		/// 资源释放
		/// </summary>
		public void Dispose()
		{
			_keepRun = null;

			// 停止预览
			CHCNetSDK.NET_DVR_StopRealPlay(m_lRealHandle);

			// 撤销报警上传通道
			CHCNetSDK.NET_DVR_CloseAlarmChan_V30(m_lAlarmHandle);

			// 用户注销
			CHCNetSDK.NET_DVR_Logout(m_lUserID);

			// 释放 SDK 资源 在结束后调用
			CHCNetSDK.NET_DVR_Cleanup();

			LogEx.Debug("海康视频资源释放.");
		}

		#endregion
	}
}